#include <ymodem.h>
#include <esp_timer.h>

void start()
{

    ESP_LOGI(YMODEM_TAG, "start");
}

ymodem_status_t wait_for_char(uint8_t expected, uart_port_t uart, int timeout_ms)
{
    uint8_t byte;
    int64_t start = esp_timer_get_time() / 1000;
    ESP_LOGI("WaitForChar", "START WAITING");
    while ((esp_timer_get_time() / 1000 - start) < timeout_ms)
    {
        int len = uart_read_bytes(uart, &byte, 1, pdMS_TO_TICKS(20));
        if (len == 1)
        {
            if (byte == expected)
            {
                return YM_SUCCESS;
            }
            else if (byte == CAN)
            {
                ESP_LOGE(YMODEM_TAG, "Transfer cancelled by receiver");
                return YM_ERR_CANCELLED;
            }
        }
    }
    ESP_LOGI("WaitForChar", "END WAITING");
    return YM_ERR_TIMEOUT;
}

ymodem_status_t ymodem_send_file(const char *filename, const ymodem_config_t *config, FILE *file, int32_t file_size)
{

    ESP_LOGI("DOWNLOAD_FILE", "DOWNLOAD_FILESIZE:%ld", file_size);

    ESP_LOGI(YMODEM_TAG, "WAITING FOR RECEIVER 'C' Signal ....");
    ymodem_status_t status = wait_for_char(CRC_CHAR, config->uart_port, DEFAULT_TIMEOUT);
    if (status != YM_SUCCESS)
    {
        ESP_LOGI(YMODEM_TAG, "first receive err %2x", status);
        return YM_ERR_TIMEOUT;
    }
    ESP_LOGI(YMODEM_TAG, "RECEIVER 'C' Signal SUCCESS");

    // 发送第一个文件块 共133个字节 帧头家帧序号 SOH+ NUM+ ~NUM+ 128+  CRC-L  CRC-H  133个字节

    uint8_t header_block[133] = {0};
    header_block[0] = SOH;
    header_block[1] = 0x00;
    header_block[2] = 0xFF;

    const char *base_name = strrchr(filename, '/');
    base_name = base_name ? base_name + 1 : filename;
    size_t filename_len = strlen(base_name);

    // 复制文件名（包含终止符0x00）
    if (filename_len + 1 <= 128)
    { // 确保有足够空间
        memcpy(header_block + 3, base_name, filename_len + 1);
    }

    // 计算文件大小的字符串表示
    char file_size_str[20]; // 足够存储文件大小的字符串
    snprintf(file_size_str, sizeof(file_size_str), "%ld", file_size);
    size_t filesize_len = strlen(file_size_str);

    // 找到文件名后的第一个0x00位置
    size_t filename_end = 3;
    while (filename_end < 131 && header_block[filename_end] != 0x00)
    {
        filename_end++;
    }

    // 在文件名后的0x00之后，复制文件大小（包含终止符0x00）
    size_t remaining_space = 131 - filename_end - 1; // 减去当前0x00
    if (filesize_len + 1 <= remaining_space)
    {
        memcpy(header_block + filename_end + 1, file_size_str, filesize_len + 1);
    }

    // 确保整个文件信息块（128字节）被填充0x00
    for (int i = 3; i < 131; i++)
    {
        if (i > filename_end + filesize_len + 1)
        {
            header_block[i] = 0x00;
        }
    }

    uint16_t crc = calculate_crc16(header_block + 3, 128);
    header_block[131] = (crc >> 8) & 0xFF;
    header_block[132] = crc & 0xFF;

    uart_write_bytes(config->uart_port, (const char *)header_block, sizeof(header_block));
    // 这里开始收到确认消息之后 开始发送文件块
    if (wait_for_char(ACK, config->uart_port, config->tx_timeout_ms) != YM_SUCCESS)
    {
        // fclose(file);
        return YM_ERR_TIMEOUT;
    }
    else
    {
        ESP_LOGI(YMODEM_TAG, "START Transmit file!!!!!!!!!");
    }

    uint32_t block_size = config->block_size;
    uint32_t total_blocks = (file_size + block_size - 1) / block_size; // 向上取整
    uint32_t last_block_size = file_size % block_size;
    if (last_block_size == 0)
    {
        last_block_size = block_size; // 整除时最后一块为完整块
    }

    ESP_LOGI(YMODEM_TAG, "Total blocks: %ld, last block size: %ld", total_blocks, last_block_size);

    // 发送数据块
        // 发送数据块
    uint8_t block_num = 1;
    uint8_t data_block[1029]; // STX + blocknum + ~blocknum + 1024 data + 2 CRC
    size_t bytes_read;
    int retry_count = 0;
    
    while ((bytes_read = fread(data_block + 3, 1, config->block_size, file)) > 0) {
        // 不足块大小的部分填充0x1A (SUB)
        if (bytes_read < config->block_size) {
            memset(data_block + 3 + bytes_read, 0x1A, config->block_size - bytes_read);
        }
        
        data_block[0] = (config->block_size == 1024) ? STX : SOH;
        data_block[1] = block_num;
        data_block[2] = ~block_num;
        
        crc = calculate_crc16(data_block + 3, config->block_size);
        data_block[3 + config->block_size] = (crc >> 8) & 0xFF;
        data_block[4 + config->block_size] = crc & 0xFF;
        
        size_t block_len = (data_block[0] == STX) ? 1029 : 133;
        
        retry:
        uart_write_bytes(config->uart_port, (const char *)data_block, block_len);
        
        // 等待ACK
        ymodem_status_t status = wait_for_char(ACK, config->uart_port, config->tx_timeout_ms);
        if (status == YM_SUCCESS) {
            block_num = (block_num + 1) % 256; // 块号循环
            retry_count = 0;
            continue;
        }
        
        // 处理错误
        if (++retry_count > config->max_retries) {
            uart_write_bytes(config->uart_port, (const char[]){CAN, CAN}, 2);
            fclose(file);
            return status;
        }
        goto retry;
    }

    // 发送结束信号
    int eot_retry = 0;
    while (eot_retry++ < 3) {
        uart_write_bytes(config->uart_port, (const char[]){EOT}, 1);
        if (wait_for_char(ACK, config->uart_port, config->tx_timeout_ms) == YM_SUCCESS) {
            break;
        }
    }

    // 新增：发送空Block 0（告知无更多文件）
    ESP_LOGI(YMODEM_TAG, "Sending final empty block (end of transfer)");
    uint8_t final_block[133] = {0};
    final_block[0] = SOH;
    final_block[1] = 0x00;              // Block 0
    final_block[2] = 0xFF;              // ~Block 0
    memset(final_block + 3, 0x00, 128); // 空数据
    uint16_t final_crc = calculate_crc16(final_block + 3, 128);
    final_block[131] = (final_crc >> 8) & 0xFF;
    final_block[132] = final_crc & 0xFF;

    // 等待接收方ACK确认空块
    if (uart_write_bytes(config->uart_port, (const char *)final_block, sizeof(final_block)) != sizeof(final_block))
    {
        fclose(file);
        return YM_ERR_IO;
    }
    if (wait_for_char(ACK, config->uart_port, config->tx_timeout_ms) != YM_SUCCESS)
    {
        fclose(file);
        return YM_ERR_TIMEOUT;
    }

    ESP_LOGI(YMODEM_TAG, "YModem transfer completed successfully");
    fclose(file);
    return YM_SUCCESS;
}

/**
 * 计算单个字节的CRC校验值
 * @param crc 当前CRC值
 * @param data 输入字节
 * @return 更新后的CRC值
 */
uint16_t update_crc(uint16_t crc, uint8_t data)
{
    crc ^= (uint16_t)data << 8;
    for (int i = 0; i < 8; i++)
    {
        if (crc & 0x8000)
        {
            crc = (crc << 1) ^ 0x1021; // 多项式0x1021
        }
        else
        {
            crc <<= 1;
        }
    }
    return crc;
}

/**
 * 计算数据块的CRC-16校验值
 * @param data 数据指针
 * @param length 数据长度(字节)
 * @return CRC-16校验值(低字节在前,高字节在后)
 */
uint16_t calculate_crc16(const uint8_t *data, uint32_t length)
{
    uint16_t crc = 0x0000; // 初始值为0

    for (uint32_t i = 0; i < length; i++)
    {
        uint8_t index = ((crc >> 8) ^ data[i]) & 0xFF;
        crc = (crc << 8) ^ crc_table[index];
    }

    return crc;
}
